package worker;

import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.PriorityQueue;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import common.JobObject;
import common.JobRequestObject;
import common.Util;
import common.WorkerObject;


public class MasterRequestThread extends Thread {
	
	private WorkerObject worker;
	private WorkerService workerService;
	private PriorityQueue<JobObject> jobQueue;
	
	public MasterRequestThread(WorkerObject worker, WorkerService workerService, PriorityQueue<JobObject> jobQueue) {
		this.workerService = workerService;
		this.jobQueue = jobQueue;
		this.worker = worker;
	}
	
	public void run() {
		try{
			//			SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
			//			SSLServerSocket listenSocket = (SSLServerSocket) sslserversocketfactory.createServerSocket(managerTCPPort);
			ServerSocket listenSocket = new ServerSocket(worker.getPort());

			while (true) {
				Socket masterSocket = listenSocket.accept();
				//				SSLSocket clientSocket = (SSLSocket) listenSocket.accept();
				System.out.println("SYSINFO - Accept Master TCP connection Request.");
				
				DoRequest doRequest = new DoRequest(masterSocket, workerService, jobQueue);
				doRequest.start();

			}
		}catch(IOException e){System.out.println("Listen:"+e.getMessage());}
	}
}

class DoRequest extends Thread {
	private Socket masterSocket;
	WorkerService workerService;
	PriorityQueue<JobObject> jobQueue;
	static final String WAITING = "WAITING";
	
	public DoRequest(Socket masterSocket, WorkerService workerService, PriorityQueue<JobObject> jobQueue) {
		this.masterSocket = masterSocket;
		this.workerService = workerService;
		this.jobQueue = jobQueue;
	}
	
	public void run() {
		try {
			InputStream in = masterSocket.getInputStream();
			DataInputStream dataIn = new DataInputStream(in);
	
			OutputStream out = masterSocket.getOutputStream();
			DataOutputStream dataOut = new DataOutputStream(out);
			
			String request = dataIn.readUTF();
			System.out.println("SYSINFO - Received Master request.");
			System.out.println("DEBUG - xml recevied: " + request);
			
			Document inDoc = Util.stringToDoc(request);
			Element inRoot = inDoc.getDocumentElement();
			
			String reply = null;
			if (inRoot.getAttribute("type").equals("Login")) {
				System.out.println("DEBUG - It is a login request.");
				//reply = doLogin(inDoc, clientSocket);
				reply = doLogin(request, dataOut, masterSocket);
			} 
			else if (inRoot.getAttribute("type").equals("SendJob")) {
				//SendJobs -- start to schedule immediately
				System.out.println("DEBUG - It is a SendJob request.");
				//reply = doSendJobs(inDoc);
				reply = doSendJob(request,dataIn);
			} 
			else if (inRoot.getAttribute("type").equals("RetrieveOutput")) {
				System.out.println("DEBUG - It is a RetrieveOutput request.");
				reply = doRetrieveOutput(inDoc, dataOut);
			} 
			else if (inRoot.getAttribute("type").equals("QueryJob")) {
				System.out.println("DEBUG - It is a QueryJobs request.");
				reply = doQueryJobs(inDoc);
			} else if (inRoot.getAttribute("type").equals("KillJob")) {
				System.out.println("DEBUG - It is a KillJobs request.");
				reply = doKillJob(inDoc);
			} else {
				System.out.println("SYSINFO - It is NOT a valid request. No action will be performed.");
			}
			
			if (reply!=null) dataOut.writeUTF(reply);
			System.out.println("SYSINFO - Send reply.");
			dataOut.close();
			
		} catch(IOException e){
			System.out.println("Conection:"+e.getMessage());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} finally{
			try{
				if(masterSocket != null) masterSocket.close();
			}
			catch(IOException e){}
		}
	}
	
	private String doSendJob(String jobRequestXML, DataInputStream dataIn) {
		JobObject job = null;
		try {
			/********** JobPackage Information Transfer **********/
			JAXBContext context = JAXBContext.newInstance(JobRequestObject.class);
			Unmarshaller u = context.createUnmarshaller();
			StringBuffer xmlStr = new StringBuffer(jobRequestXML);
			JobRequestObject JobRequest = (JobRequestObject) u.unmarshal(new StreamSource(new StringReader(xmlStr.toString())));
			if (JobRequest.getFrom().equals("Master") && JobRequest.getType().equals("SendJob")) {
				job = JobRequest.getJobObject();

				Long fileSize = dataIn.readLong();

				System.out.println("DEBUG - fileName = " + job.getExecuteFileName());
				System.out.println("DEBUG - parameters = "	+ job.getParameters());
				System.out.println("DEBUG - time = " + job.getTime());
				System.out.println("DEBUG - time = " + job.getExtraTime());
				System.out.println("DEBUG - jobID = " + job.getJobID());
				System.out.println("DEBUG - priority = " + job.getPriority());
				System.out.println("DEBUG - status = " + job.getStatus());
				System.out.println("DEBUG - clientID = " + job.getClientID());
				System.out.println("DEBUG - fileSize = " + fileSize);

				String filePath = "workerJobs"+"/"+job.getClientID(); // default jobs folder (but can be assign to another one)
				System.out.println(filePath);
				File file = new File(filePath);
				if (file.exists()==false) {
					file.mkdirs();
				}
				job.setExecuteFilePath(filePath);
				jobQueue.add(job);
			} else {
				System.out.println("SYSINFO - The request is not a JobRequest. No action will be performed.");
			}
			
			/********** File Transfer **********/
			// NTD: handle multiple files transfer
			String savePath = job.getExecuteFilePath();
			int bufferSize = 8192;
			byte[] buf = new byte[bufferSize];
			savePath += "/"+job.getExecuteFileName()+".class";
			DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(savePath)));

			int length = 0;
			while((length = dataIn.read(buf))!=-1) {
				fileOut.write(buf, 0, length);
			}
			System.out.println("File saved at " + savePath + "");
			fileOut.close();
			
		} catch(EOFException e) {System.out.println("EOF:"+e.getMessage());
		} catch(IOException e) {System.out.println("IO:"+e.getMessage());
		} catch (Exception e) {e.printStackTrace();
		}
		return null;
	}

	private String doQueryJobs(Document inDoc) {
		return null;
	}

    private String doKillJob(Document inDoc){
        Worker.killJob = true;
//       int jobID = Integer.parseInt(inDoc.getElementsByTagName("jobID").item(0).getTextContent());
//        workerService.KillExecuteJobsThread(jobID);
        return null;
    }

	private String doRetrieveOutput(Document inDoc, DataOutputStream dataOut) {
		return null;
	}
	
	private String doLogin(String request, DataOutputStream dataOut, Socket clientSocket) {
		return null;
	}
}
